//==============================================================================
//
//           DTimer - the range data type in the ofc-library
//
//               Copyright (C) 2003  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-03 18:31:55 $ $Revision: 1.10 $
//
//==============================================================================

#include "ofc/DTimer.h"

#ifdef WIN32
#include <windows.h>
#else
#include <sys/time.h>
#endif


#if _INTERFACE_

#include "ofc/config.h"

//
// The timer class implements a number of methods for creating and polling a
// timer. Maximum resolution: << 10000 seconds. The class can also be used to 
// delay execution for a while.
//
// @example
// #include <stdio.h>
// #include "ofc/DTimer.h"
// 
// int main(int argc, char *argv[])
// {
//   DTimer *tmr = [DTimer alloc];
// 
//   [tmr init :150];                     // Init with a timeout value of 150 msec
// 
//   [tmr restart];                       // Restart the timer
// 
//   [DTimer delay :80];                  // Wait 80 msec
//                                        // Check if the timer is expired
//   printf("Timer %s expired.\n", ([tmr isExpired] ? "is" : "is not"));
//   
//   printf("Timer time:%ld.\n", [tmr timer]); // Get the current timer time
// 
//   [DTimer delay :80];                  // Wait another 80 msec -> 160 msec waited
//                                        // Check again if the timer is expired
//   printf("Timer %s expired.\n", ([tmr isExpired] ? "is" : "is not"));
// 
//   [tmr free];                          // Cleanup
// 
//   return 0;
// }
// 

@interface DTimer : DObject
{
@private
  long         _now;       // last timer check in msec
  long         _timeOut;   // time out value in msec
  long         _remaining; // remaing msec during last timer check
}

#endif



#define USECS_PER_MSEC       1000L
#define MSECS_PER_SEC        1000L
#define SECS_PER_TIMER       10000L
#define MSECS_PER_TIMER     (MSECS_PER_SEC * SECS_PER_TIMER)



@implementation DTimer

static long getTime()
{
#ifdef WIN32
  return GetTickCount();
#else
  struct timeval tv;
  
  gettimeofday(&tv, NULL);

  return ((tv.tv_usec / USECS_PER_MSEC) + (tv.tv_sec % SECS_PER_TIMER) * MSECS_PER_SEC);
#endif
}


//// Class Methods

//
// Delay for a certain time
//
// @param msec     the number of milliseconds to wait
//
+ (void) delay :(long) msec
{
  if (msec > 0)
  {
#ifdef WIN32
    Sleep(msec);
#else
    struct timeval tv;

    getTime();
    
    tv.tv_sec  =  msec / MSECS_PER_SEC;
    tv.tv_usec = (msec % MSECS_PER_SEC) * USECS_PER_MSEC;

    select(0, NULL, NULL, NULL, &tv);
    
    getTime();
#endif
  }
  else
    WARNING(DW_INVALID_ARG, "msec");
}


//// Constructors

//
// Initialise a default timer 
//
// @return the object
//
- (DTimer *) init
{
  [super init];

  _now       = getTime();
  _timeOut   = 0;
  _remaining = 0;
  
  return self;
}

//
// Initialise the timer with a timer-out value
//
// @param timeOut  the time out value (in milliseconds)
//
// @return the object
//
- (DTimer *) init :(long) timeOut
{
  [[self init] timeOut :timeOut];
  
  return self;
}


//// Members

//
// Get the timer value (time since last expire/restart)
//
// @return the timer (in millseconds)
//
- (long) timer
{
  long diff;
  long now;

  now  = getTime();
  diff = now - _now;

  if (diff < 0) 
    diff += MSECS_PER_TIMER;

  return (diff + _remaining);
}
   
//
// Get the time out value (in milliseconds)
//
// @return the time out value (0 for uninitialised)
//
- (long) timeOut
{
  return _timeOut;
}

//
// Set the time out value (in milliseconds)
//
// @param timeOut  the time out value
//
// @return the object
//
- (DTimer *) timeOut :(long) timeOut
{
  if (timeOut > 0) 
    _timeOut = timeOut;
  else
    WARNING(DW_INVALID_ARG, "timeOut");
    
  return self;
}


//// Main methods

//
// Restart the timer
//
// @return the object
//
- (DTimer *) restart
{
  _remaining = 0L;
  _now       = getTime();

  return self;
}

//
// Test for timer expiration; if so the timer is reset
//
// @return Is the timer expired
//
- (BOOL) isExpired
{
  return [self isExpired :_timeOut];
}

//
// Test for timed expiration; if so the timer is reset
//
// @param timeOut  the time out value (in milliseconds)
//
// @return is the timer expired ?
//
- (BOOL) isExpired :(long) timeOut
{
  long diff;
  long now;

  now  = getTime();
  diff = now - _now;

  if (diff < 0) 
    diff += MSECS_PER_TIMER;

  if ((diff + _remaining) >= timeOut)
  {
    _remaining = (diff + _remaining) - timeOut;
    _now       = now;

    return YES;
  }
  return NO;
}
 
@end

/*==========================================================================*/
